from threading import Timer

import serial
import serial.tools.list_ports
import queue
import zlib
import threading


from log import *
from cmd import *

init(autoreset=True)
import time


class SerialWorker:
    def __init__(self, _port, baudrate=921600, timeout=0.000):
        self.ser = serial.Serial(_port, baudrate, timeout=timeout)
        self.read_thread = threading.Thread(target=self._read_loop, daemon=True)
        self.write_thread = threading.Thread(target=self._write_loop, daemon=True)
        self.write_queue = queue.Queue()
        self.running = False
        self.state = DownAPPState.DOWN_BOOT_DEFALUT
        self._state_lock = threading.Lock()  # 线程安全
        self.chunks=list()
        self.crc = 0
        self.total_size = 0
        self.send_count =0
        self.cmd = CommandParser()
        self.start_time = None

    def start(self):
        self.running = True
        self.read_thread.start()
        self.write_thread.start()
        info(f"Serial worker started on {self.ser.port}")

    def stop(self):
        self.running = False
        time.sleep(0.1)
        if self.ser.is_open:
            self.ser.close()
        info("Serial worker stopped")

    def send(self, data: bytes):
        #info("TX: " + data.hex(' ').upper())
        self.write_queue.put(data)

    def _write_loop(self):
        while self.running:
            try:
                data = self.write_queue.get(timeout=0.1)
                self.ser.write(data)
            except queue.Empty:
                continue
            except serial.SerialException as e:
                error(f"Serial write error: {e}")
                break

    def _read_loop(self):
        while self.running:
            try:
                data = self.ser.readline()
                if not data:
                    continue
                #debug(f"RX: {data.hex(' ').upper()}")
                for b in data:
                    self.cmd.cmd(b)
                ret = self.cmd.process_frame()
               # ret = self.parse_frame(data)
                if ret:
                    try:
                        with self._state_lock:
                            try:
                                if self.state == DownAPPState.DOWN_BOOT_DEFALUT:
                                    self.start_time.cancel()
                                # 只有在不是 CHECK_FIRMWARE 或已发送完 chunks 才更新状态
                                if self.state != DownAPPState.DOWN_APP or len(self.chunks) == self.send_count:
                                    self.state = DownAPPState(ret['cmd'] + 1)
                            except ValueError:
                                debug(f"未知状态 cmd={ret['cmd'] + 1}")
                    except ValueError:
                        debug(f"未知状态 cmd={ret['cmd'] + 1}")
                    else:
                        if ret.get('result') :
                           # debug(f"RX: {ret}")
                            self.down()
                        else:
                            warning(f"RX: {ret}")
                else:
                    warning(f"RX: {data}")
            except serial.SerialException as e:
                error(f"Serial read error: {e}")
                break

    def calculate_crc32(self):
        """计算文件的CRC32校验值"""
        try:
            crc_value = 0
            for chunk in self.chunks:
                crc_value = zlib.crc32(chunk, crc_value)
            return crc_value & 0xFFFFFFFF  # 确保返回无符号32位整数
        except Exception as e:
            print(f"计算CRC时出错: {e}")
            return None



    def down(self):
        #with self._state_lock:
        temp = b''
        sateValue = self.state.value
        if self.state == DownAPPState.DOWN_BOOT_DEFALUT:
            self.start_time = threading.Timer(0.1, lambda:self.down())
            self.start_time.start()
            self.send_count = 0
            info(f"STEP_{self.state.value}: {self.state.name}")
        elif self.state == DownAPPState.DOWN_KEY:
            self.chunks = read_bin_chunks("F:/bootloader_-stm32/TESTAPP/MDK-ARM/bin_file/TESTAPP.bin")
            self.crc = self.calculate_crc32()

            self.total_size = sum(len(c) for c in self.chunks)
            info(f"总共 {len(self.chunks)} 块, 数据总大小: {self.total_size} 字节  crc: 0x{self.crc:08X}")

            total_size = sum(len(c) for c in self.chunks)
            temp = total_size.to_bytes(4, byteorder='big')
        elif self.state == DownAPPState.DOWN_APP:
            if self.send_count < len(self.chunks):
                temp = bytes([self.send_count]) + self.chunks[self.send_count]
                self.send_count += 1
            else:
                debug("所有 chunks 已发送完")
            #info( f"STEP_{self.state.value}: {self.state.name} cnt:{self.send_count} temp:{temp[:16].hex(' ').upper()}")

        else:
            info(f"STEP_{self.state.value}: {self.state.name}")

        frame = get_frame(sateValue, temp)
        self.send(frame)



if __name__ == "__main__":
    ports = serial.tools.list_ports.comports()
    if not ports:
        print("No serial ports found.")
        exit(1)

    print("Available ports:")
    for p in ports:
        print(f" {p.device}")

    port = "COM3"  # 或自动选择 ports[0].device
    info(f"Using port: {port}")
    worker = SerialWorker(port)
    worker.start()

    try:

        worker.down()
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        info("Keyboard interrupt received")
        worker.stop()
        print("Stopped.")